/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.explorer.view; import java.beans.*; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; import javax.swing.SwingUtilities; import javax.swing.event.*; import javax.swing.tree.*; import javax.swing.*; import org.openide.util.*; import org.openide.nodes.Node; import org.openide.nodes.NodeAdapter; import org.openide.nodes.NodeListener; import org.openide.nodes.NodeEvent; import org.openide.nodes.NodeMemberEvent; import org.openide.nodes.NodeReorderEvent; import org.openide.nodes.Children; /** Model for displaying the nodes in list and choice. * * @author Jaroslav Tulach */ public class NodeListModel extends AbstractListModel implements ComboBoxModel { /** listener used to listen to changes in trees */ private transient Listener listener; /** parent node */ private transient VisualizerNode parent; /** originally selected item */ private transient Object selectedObject; /** previous size */ private transient int size; /** depth to display */ private int depth = 1; /** map that assignes to each visualizer number of its children till * the specified depth. (VisualizerNode, Info) * @associates Info */ private Map childrenCount; static final long serialVersionUID =-1926931095895356820L; /** Creates new NodeTreeModel */ public NodeListModel () { parent = VisualizerNode.EMPTY; selectedObject = VisualizerNode.EMPTY; clearChildrenCount (); } /** Creates new NodeTreeModel * @param root the root of the model */ public NodeListModel (Node root) { parent = VisualizerNode.EMPTY; setNode (root); } /** Changes the root of the model. This is thread safe method. * @param root the root of the model */ public void setNode (final Node root) { Mutex.EVENT.readAccess (new Runnable () { public void run () { VisualizerNode v = VisualizerNode.getVisualizer (null, root); if (v == parent) { // no change return; } removeAll (); parent.removeNodeModel (listener ()); parent = v; selectedObject = v; clearChildrenCount (); parent.addNodeModel (listener ()); addAll (); } }); } /** Depth of nodes to display. * @param depth the depth */ public void setDepth (int depth) { if (depth != this.depth) { this.depth = depth; clearChildrenCount (); Mutex.EVENT.readAccess (new Runnable () { public void run () { removeAll (); addAll (); } }); } } /** Getter for depth. * @return number of levels to display */ public int getDepth () { return depth; } /** Getter for the listener. Only from AWT-QUEUE. */ private Listener listener () { if (listener == null) { listener = new Listener (this); } return listener; } // // model methods // /** Number of elements in the model. */ public int getSize () { int s = findSize (parent, -1, depth); return s; } /** Child at given index. */ public Object getElementAt (int i) { return findElementAt (parent, i, -1, depth); } /** Finds index of given object. * @param obj object produced by this model * @return if the object is not in the list, then return -1 */ public int getIndex (Object o) { getSize (); Info i = (Info)childrenCount.get (o); return i == null ? -1 : i.index; } /** Currently selected item. */ public void setSelectedItem (Object anObject) { if ( selectedObject != anObject ) { selectedObject = anObject; fireContentsChanged(this, -1, -1); } } public Object getSelectedItem() { return selectedObject; } // // modification of the counting model // private void clearChildrenCount () { childrenCount = new HashMap (17); } /** Finds size of sub children excluding vis node. * * @param vis the visualizer to find the size for * @param index the index that should be assigned to vis * @param depth the depth to scan * @return number of children */ private int findSize (VisualizerNode vis, int index, int depth) { Info info = (Info)childrenCount.get (vis); if (info != null) { return info.childrenCount; } // only my children int size = 0; info = new Info (); info.depth = depth; info.index = index; if (depth-- > 0) { Iterator it = vis.getChildren ().iterator (); while (it.hasNext ()) { VisualizerNode v = (VisualizerNode)it.next (); // count node v size++; // now count children of node v size += findSize (v, index + size, depth); } } info.childrenCount = size; childrenCount.put (vis, info); return size; } /** Finds the child with requested index. * * @param vis the visualizer to find the size for * @param indx the index of requested child * @param depth the depth to scan * @return the children */ private VisualizerNode findElementAt ( VisualizerNode vis, int indx, int realIndx, int depth ) { if (--depth == 0) { // last depth is handled in special way return (VisualizerNode)vis.getChildAt(indx); } Iterator it = vis.getChildren ().iterator (); while (it.hasNext ()) { VisualizerNode v = (VisualizerNode)it.next (); if (indx-- == 0) { return v; } int s = findSize (v, ++realIndx, depth); if (indx < s) { // search this child return findElementAt (v, indx, realIndx, depth); } // go to next child indx -= s; realIndx += s; } return vis; } /** Finds a depth for given model & object. Used from renderer. * @param m model * @param o the visualizer node * @return depth or 0 if not found */ static int findVisualizerDepth (ListModel m, VisualizerNode o) { if (m instanceof NodeListModel) { NodeListModel n = (NodeListModel)m; Info i = (Info)n.childrenCount.get (o); if (i != null) { return n.depth - i.depth - 1; } } return 0; } // // Modifications // final void addAll () { size = getSize (); if (size > 0) { fireIntervalAdded (this, 0, size - 1); } } final void removeAll () { if (size > 0) { fireIntervalRemoved (this, 0, size - 1); } } final void changeAll () { if (size > 0) { fireContentsChanged (this, 0, size - 1); } clearChildrenCount (); } final void added (VisualizerEvent.Added ev) { int[] indices = ev.getArray (); if (indices.length == 1) { // special handling size++; fireIntervalAdded (this, indices[0], indices[0]); } else { removeAll (); clearChildrenCount (); addAll (); } clearChildrenCount (); } final void removed (VisualizerEvent.Removed ev) { int[] indices = ev.getArray (); if (indices.length == 1) { // special handling size--; fireIntervalRemoved (this, indices[0], indices[0]); } else { removeAll (); addAll (); } clearChildrenCount (); } final void update (VisualizerNode v) { // ensure the model is computed getSize (); Info i = (Info)childrenCount.get (v); if (i != null) { fireContentsChanged (this, i.index, i.index); } } /** The listener */ private static final class Listener implements NodeModel { /** weak reference to the model */ private Reference model; /** Constructor. */ public Listener (NodeListModel m) { model = new WeakReference (m); } /** Getter for the model or null. */ private NodeListModel get (VisualizerEvent ev) { NodeListModel m = (NodeListModel)model.get (); if (m == null && ev != null) { ev.getVisualizer ().removeNodeModel (this); return null; } return m; } /** Notification of children addded event. Modifies the list of nodes * and fires info to all listeners. */ public void added (VisualizerEvent.Added ev) { NodeListModel m = get (ev); if (m == null) return; m.added (ev); } /** Notification that children has been removed. Modifies the list of nodes * and fires info to all listeners. */ public void removed (VisualizerEvent.Removed ev) { NodeListModel m = get (ev); if (m == null) return; m.removed (ev); } /** Notification that children has been reordered. Modifies the list of nodes * and fires info to all listeners. */ public void reordered (VisualizerEvent.Reordered ev) { NodeListModel m = get (ev); if (m == null) return; m.changeAll (); } /** Update a visualizer (change of name, icon, description, etc.) */ public void update(VisualizerNode v) { NodeListModel m = get (null); if (m == null) return; m.update (v); } } /** Info for a component in model */ private static final class Info extends Object { public int childrenCount; public int depth; public int index; public String toString () { return "Info[childrenCount=" + childrenCount + ", depth=" + depth + // NOI18N ", index=" + index; // NOI18N } } } /* * Log * 9 Gandalf 1.8 1/12/00 Ian Formanek NOI18N * 8 Gandalf 1.7 12/3/99 Jaroslav Tulach #4864 * 7 Gandalf 1.6 11/29/99 Jaroslav Tulach Initilized even no root * set. * 6 Gandalf 1.5 11/26/99 Patrik Knakal * 5 Gandalf 1.4 11/24/99 Jaroslav Tulach Keeps nodes in memory. * 4 Gandalf 1.3 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 3 Gandalf 1.2 10/6/99 Jaroslav Tulach #4068 * 2 Gandalf 1.1 8/27/99 Jaroslav Tulach List model can display * more levels at once. * 1 Gandalf 1.0 8/27/99 Jaroslav Tulach * $ */